package com.mhs.component;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mhs.entity.Chat;
import com.mhs.entity.Message;
import com.mhs.mapper.IChatMapper;
import com.mhs.mapper.IMessageMapper;
import javafx.util.converter.LocalDateTimeStringConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;


/**
 *
 */
@ServerEndpoint(value = "/mhs/chat/{userid}")
@Component
public class WebSocketServer {
    static IMessageMapper messageMapper;
    static IChatMapper chatMapper;


    public static void setMessageMapper(IMessageMapper messageMapper) {
        WebSocketServer.messageMapper = messageMapper;
    }
    public static void setChatMapper(IChatMapper chatMapper) {
        WebSocketServer.chatMapper = chatMapper;
    }


    //用来记录哪些用户有新消息但当前未在线，以便用户上线时推送这些消息。
    private static List<Long> haveNew = Collections.synchronizedList(new ArrayList<>());
    //用于消息暂存及操作Redis数据库
    static RedisTemplate redisTemplate;

    public static void setRedisTemplate(RedisTemplate redisTemplate) {
        WebSocketServer.redisTemplate = redisTemplate;
    }

    /**
     * 记录在线咨询师人数,存储已连接用户的Session对象，键为用户ID，值为对应的WebSocket会话
     */
    public static Map<Long, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 登录成功自动连接webSocket
     * 将webSocket的session对象存起来
     * 当客户端成功建立WebSocket连接时触发onOpen。
     * 当客户端成功建立WebSocket连接时触发。这里根据用户ID将Session添加到sessionMap中，并检查该用户是否有未读消息（存储在Redis中）
     * 如果有，则将消息从Redis取出并通过WebSocket发送给客户端，最后清空Redis中的缓存消息。
     * @param session
     * @param userId
     */
    //把消息链表改革方向 将新界收到的消息存数据库
    @OnOpen
    public void onOpen(Session session, @PathParam("userid") Long userId) throws IOException {
        if (haveNew.contains(userId)) {//判断当前用户是否有未读消息(存储在Redis)
            List range = redisTemplate.opsForList().range(userId.toString(), 0, -1);
            System.out.println("==========");
            System.out.println(range);
            for (Object o : range) {
                Message ma = (Message) o;
                String jsonString = JSON.toJSONString(ma);
                RemoteEndpoint.Basic basicRemote = session.getBasicRemote();
                System.out.println(jsonString);
                basicRemote.sendText(jsonString);
            }
            //将redis里缓存的消息删除
            redisTemplate.delete(userId.toString());
        }

        sessionMap.put(userId, session);
    }

    @OnClose
    public void onClose(Session session, @PathParam("userid") Long userid) {

        sessionMap.remove(userid);
    }

    /**
     * 处理接收到的WebSocket消息。解析消息内容，执行逻辑判断，如根据接收方用户ID判断其是否在线。
     * 若在线，则直接转发消息；若不在线，则将消息暂存至Redis，并在用户上线时从Redis推送这些消息。
     * 同时，该方法还负责检查和创建聊天记录（调用checkAndAddChat方法）。
     * @param message
     * @param session
     * @throws IOException
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        if (redisTemplate == null) {
            throw new RuntimeException("注入失败");
        }
        System.out.println(message);
        JSONObject json = JSONObject.parseObject(message);
        System.out.println(json.toJSONString());
        //   System.out.println(json.get("fromUid") + "    from");
        Long toUid = Long.parseLong((String) json.get("toUid"));
        Long fromUid = Long.parseLong((String) json.get("fromUid"));
        String content = (String) json.get("content");
        //得到message对象的toUid从登录的用户集合找到session再由服务端向客户端发送消息如果用户没上线则存redis
        //接收端用户没上线将该消息存入redis

        Message message1 = new Message();
        message1.setFromUid(fromUid);
        message1.setToUid(toUid);
        message1.setContent(content);


        message1.setCreatTime(LocalDateTime.now());
        if (!sessionMap.containsKey(toUid)) {
            System.out.println("用户" + toUid + "没有上线");
            //将该用户id加入List
            haveNew.add(toUid);
            //判断是否有创建聊天室chat
            checkAndAddChat(toUid,fromUid);
//            redisTemplate.opsForList().leftPushAll();
            //使用redis暂存消息
            redisTemplate.opsForList().rightPush(toUid.toString(), message1);
            messageMapper.insertMessage(message1.getContent(), message1.getFromUid(), message1.getToUid(), message1.getCreatTime());

        }

        for (Map.Entry<Long, Session> entry : sessionMap.entrySet()) {
            if (entry.getKey().equals(toUid)) {
                RemoteEndpoint.Basic basicRemote = entry.getValue().getBasicRemote();
                basicRemote.sendText(JSON.toJSONString(message1));
                checkAndAddChat(toUid,fromUid);
                //将成功发送的消息存入数据库
                messageMapper.insertMessage(content, fromUid, toUid, LocalDateTime.now());
            }

        }

    }

    /**
     * 检查是否接受者和发送者之间有没有聊天记录(聊天室chat)
     * @param toUid
     * @param fromUid
     */
    public void checkAndAddChat(Long toUid,Long fromUid){
        LambdaQueryWrapper<Chat> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Chat::getConsultantId,toUid).eq(Chat::getConsumerId,fromUid);
        List<Chat> chats = chatMapper.selectList(queryWrapper);
        queryWrapper.clear();
        queryWrapper.eq(Chat::getConsultantId,fromUid).eq(Chat::getConsumerId,toUid);
        List<Chat> chats2 = chatMapper.selectList(queryWrapper);
        if ((chats == null && chats2 == null) || (chats.isEmpty() && chats2.isEmpty()) ) {
            Chat chat = new Chat();
            chat.setCreateTime(LocalDateTime.now());
            chat.setConsultantId(toUid);
            chat.setConsumerId(fromUid);
            chatMapper.insert(chat);
        }
    }


}

